home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-01-15 | 35.2 KB | 1,030 lines |
- //
- //
- // shortcomings:
- // -- doesn't play enough 7th chords with missing 3, 5
- // -- since the synthesizer I'm using can only play 4 notes, we're limited to 4-note
- // chords
- // -- Doesn't know much about how to choose among different interpretations of
- // the same chord. All it knows is that if it has a m7 chord with the
- // 3 in the bass, it should reinterpret it as a 6 chord.
- // -- Should give hints about how you could have recognized the previous chord,
- // e.g., it contains an aug triad,...
- // -- when the difficulty level is very high, it still ends up playing quite
- // a few ordinary chords, because it thinks them up in some convoluted
- // way (e.g. m add b6) but then simplify_chord realizes they're
- // more ordinary (maj 7). On high difficulty levels, should usually
- // reject these chords that turn out to be ordinary, and try again.
- // -- On high difficulty levels, there is a tendency to make chords that are
- // too easy by use of the omitted notes. Wrote special cases to
- // reject some of these, e.g. 7th chords with omitted roots
- // are really triads.
- // -- choose_chord() operates by choosing a random set of allowed items that is
- // not too dissonant, then simplifying the list of items,
- // then finally making sure all the items on the new, simpler, list
- // are allowed. Unfortunately, this could result in the program never
- // playing a chord that the user wants it to play.
- // Alternatives: (1) Could revert to unsimplified form if the
- // simplified form is forbidden, but then they might never learn that,
- // e.g., a dim add b7 is the same as a half-dim. (2) When they click
- // on a check box to enable or disable an item, could try to make
- // the other check boxes consistent. E.g. if they have half-dim
- // disabled, dim enabled, and then they enable the b7 added note,
- // we could enabled half-dim as a side-effect. This would confuse them,
- // though, and they wouldn't be able to go down the list of check boxes
- // and click on them all in a row.
- // Compromise: If simplification results in a
- // reduction of number of items by one or less, and the simplified
- // version uses disabled items, go back to the more complicated
- // version.
- // -- Chords may sound to a musician more like
- // some other conventional chord but with omitted notes, e.g.
- // B dim add 11 would probably be recognized as G 13 (no 1, no 9
- // no 11).
- // -- Clicking on buttons to get rid of choices sometimes causes a chord
- // symbol to be displayed that's not what they intended. If they get
- // confused, though, they can always wipe out their guess and start over.
- //
- //
- // 29 Nov 95 - Changed fiddle_with_chord_voicing so it includes intervals of 2 and b2
- // more often.
-
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <math.h>
-
- #include <OSUtils.h>
- #include <QuickDraw.h>
- #include <Sound.h>
-
- #define NEED_MAC_STUFF 1
-
- #include "Ninkasi:C++ util:generic.h"
- #include "Ninkasi:C++ util:complete_window.h"
- #include "ear_defines.h"
- #include "ear_decl.h"
- #include "ear_prototypes.h"
-
- DEN_MOTHER_T chord_den_mother;
-
- void make_about_chord_window();
-
-
- void choose_chord(int *item,int *n,double difficulty,int *enabled_list,int n_enabled,
- int min_notes,int max_notes);
- void fiddle_with_chord_voicing(int *chord,int n,int inversions_allowed);
- int part_num_to_chord_item(int part,char *decoder_string);
-
- unsigned int plain_chord_item(unsigned int);
- double scale_difficulty(int raw);
-
- //-- had strange problems with sign-extension on & of 4-byte ints!? compiler bug!?
- #define IS_CHECK_BOX(chord_item_part) ((((unsigned) (chord_item_part)) & (unsigned) 0x8000L)!=0)
- #define PLAIN_CHORD_ITEM(chord_item_part) plain_chord_item((unsigned) (chord_item_part))
-
- #define MAXPART 200
- // highest part number in our window
-
- extern double fabs();
-
- DEN_MOTHER_T about_chord_den_mother;
- void make_window(int dlog_resource_id,
- Str255 param_text0,
- Str255 param_text1,
- Str255 param_text2,
- Str255 param_text3,
- DEN_MOTHER_T *den_mother);
-
- void
- chord_den_mother(complete_window *my_complete_window)
- {
- //--- decoder string for controls:
- static char *decoder_ptr =
-
- "\0ok,play,majt,mint,dimt,augt,sust,b5t,7,m7,maj7,o7,h,b9,9,#9,11,#11,b6,6,a7,amaj7,\
- no3,cmajt,cmint,cdimt,caugt,csust,cb5t,c7,cm7,cmaj7,co7,ch,cb9,c9,c#9,c11,c#11,\
- cb6,c6,ca7,camaj7,cno3,t1,t2,t3,ic,sc,cl,cho,ar,play_arpeggio,bass,d,lower_difficulty,\
- higher_difficulty,guess,help,no1,no5,\
- cno1,cno5,inversions,wipe,correct_answer,t4,t5,play_previous";
- //--- has to begin with null so subroutines know it's not really
- // a Pascal string
-
- static char **decoder_string = &decoder_ptr;
-
- //--- their score:
- static int ntries;
- static int nright;
- //--- keeping track of my current state:
- static int first_time = 1;
- static int they_have_chord_to_consider;
- static int current_icon;
- static int ready_to_update,need_to_redraw,need_to_redraw_all,pressed_ok,
- play_it,made_about_chord_window;
- //--- parameters that determine what chords I play and how I play them:
- #define TYPICAL_TEMPO_CHORD 45
- #define TYPICAL_TEMPO_ARPEGGIO 200
- static double typical_tempo;
- static int mean_note;
- static double raw_difficulty;
- static double difficulty;
- static int enabled_list[MAXPART];
- static int n_enabled;
- static int chord_or_arpeggio;
- static int min_notes;
- static int max_notes;
- static int inversions_allowed;
- //--- about the current chord:
- static int n_items,item[MAX_CHORD_ITEMS];
- static int n_notes,chord[MAX_CHORD_ITEMS];
- static int duration;
- static int arpeggio_direction;
- static int root;
- //--- about the previous chord:
- // These are globals because the about_chord den
- // mother needs to know about them.
- //
- // ...
- //
- //--- their guess:
- static int guess[MAX_CHORD_ITEMS];
- static int n_guess = 0;
- static int guessed_basic_part = 0;
-
- char nifty_name[100];
- int nifty_index,center,avg,i,right,they_gave_answer,cleared_scores,
- chord_item_pressed,twiddle_check_box,twiddle_radio_buttons,
- play_arpeggio,play_bass,changed_difficulty,changed_guess,
- chose_new_chord,redraw_inversion_check_box,need_to_redraw_erased_buttons,
- play_previous,activate_and_deactivate;
- GrafPtr save_graf;
-
-
- need_to_redraw = 0;
- need_to_redraw_all = 0;
- ready_to_update = 0;
- pressed_ok = 0;
- play_it = 0;
- they_gave_answer = 0;
- cleared_scores = 0;
- twiddle_check_box = 0;
- twiddle_radio_buttons = 0;
- play_arpeggio = 0;
- play_bass = 0;
- changed_difficulty = 0;
- changed_guess = 0;
- chose_new_chord = 0;
- redraw_inversion_check_box = 0;
- need_to_redraw_erased_buttons = 0;
- activate_and_deactivate = 0;
- play_previous = 0;
-
-
- if (first_time) { //-- this happens the first time our window is created,
- // and also when it's been closed and recreated
- int i,ii,ee,this_chord_item;
- first_time = 0;
- ntries = 0;
- nright = 0;
- current_icon = BLANK_ICON_ID;
- chord_or_arpeggio = 1;
- typical_tempo = TYPICAL_TEMPO_CHORD;
- mean_note = 61;
- raw_difficulty = 5;
- difficulty = scale_difficulty(raw_difficulty);
- min_notes = 3;
- max_notes = 4;
- inversions_allowed = 0;
- they_have_chord_to_consider = 0;
- have_previous_chord = 0;
- made_about_chord_window = 0;
- n_enabled = 0;
- for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
- part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
- if (nifty_index < 0) break;
- this_chord_item = part_num_to_chord_item(i,*decoder_string);
- if (this_chord_item!=0 && IS_CHECK_BOX(this_chord_item)) {
- ii = PLAIN_CHORD_ITEM(this_chord_item);
- if (ii<=HI_7TH_CHORD
- && ii!=CHORD_AUG_TRIAD
- && ii!=CHORD_SUS_TRIAD
- && ii!=CHORD_FLAT5_TRIAD
- ) {
- add_to_list(enabled_list,&n_enabled,ii);
- }
- }
- }
- }
-
- switch(my_complete_window->whassup) {
- case complete_window_created:
- need_to_redraw = 1;
- need_to_redraw_all = 1;
- break;
- case complete_window_redraw:
- need_to_redraw = 1;
- need_to_redraw_all = 1;
- ready_to_update = 1; //-- main program does begin update & sets graf port
- break;
- case complete_window_action:
- part_number_to_nifty_label(nifty_name,&nifty_index,
- *decoder_string,my_complete_window->part_number);
- chord_item_pressed = part_num_to_chord_item(
- my_complete_window->part_number,*decoder_string);
- if (nifty_index != -999) {
- if (strcmp(nifty_name,"ok")==0) {
- pressed_ok = 1;
- }
- if (strcmp(nifty_name,"play")==0) {
- play_it = 1;
- }
- if (strcmp(nifty_name,"play_previous")==0) {
- play_previous = 1;
- }
- if (strcmp(nifty_name,"wipe")==0) {
- changed_guess = 1;
- need_to_redraw = 1;
- guessed_basic_part = 0;
- activate_and_deactivate = 1;
- n_guess = 0;
- }
- if (strcmp(nifty_name,"help")==0) {
- make_chord_help_window();
- }
- if (strcmp(nifty_name,"play_arpeggio")==0) {
- play_arpeggio = 1;
- play_it = 1;
- }
- if (strcmp(nifty_name,"bass")==0) {
- play_bass = 1;
- play_it = 1;
- }
- if (strcmp(nifty_name,"cl")==0) {
- ntries = 0;
- nright = 0;
- cleared_scores = 1;
- need_to_redraw = 1;
- current_icon = BLANK_ICON_ID;
- }
- if (strcmp(nifty_name,"higher_difficulty")==0) {
- raw_difficulty += 1;
- if (raw_difficulty>20) raw_difficulty=20;
- difficulty = scale_difficulty(raw_difficulty);
- changed_difficulty = 1;
- need_to_redraw = 1;
- }
- if (strcmp(nifty_name,"lower_difficulty")==0) {
- raw_difficulty -= 1;
- if (raw_difficulty<1) raw_difficulty=1;
- difficulty = scale_difficulty(raw_difficulty);
- changed_difficulty = 1;
- need_to_redraw = 1;
- }
- if (strcmp(nifty_name,"inversions")==0) {
- need_to_redraw = 1;
- redraw_inversion_check_box = 1;
- inversions_allowed = !inversions_allowed;
- if (!inversions_allowed
- && is_in_list(enabled_list,n_enabled,CHORD_NO1)) {
- remove_from_list(enabled_list,&n_enabled,CHORD_NO1);
- twiddle_check_box = 1;
- }
- }
- if (strcmp(nifty_name,"cho")==0) {
- if (chord_or_arpeggio==2)
- duration = duration * ((double) TYPICAL_TEMPO_ARPEGGIO)
- /((double) TYPICAL_TEMPO_CHORD);
- chord_or_arpeggio = 1;
- need_to_redraw = 1;
- twiddle_radio_buttons = 1;
- typical_tempo = TYPICAL_TEMPO_CHORD;
- }
- if (strcmp(nifty_name,"ar")==0) {
- if (chord_or_arpeggio==1)
- duration = duration * ((double) TYPICAL_TEMPO_CHORD)
- /((double) TYPICAL_TEMPO_ARPEGGIO);
- chord_or_arpeggio = 2;
- need_to_redraw = 1;
- twiddle_radio_buttons = 1;
- typical_tempo = TYPICAL_TEMPO_ARPEGGIO;
- }
- if (chord_item_pressed != 0) {
- if (IS_CHECK_BOX(chord_item_pressed)) { //-- check box
- unsigned int blargh,wugga;
- twiddle_check_box = 1;
- need_to_redraw = 1;
- wugga = chord_item_pressed;
- blargh = PLAIN_CHORD_ITEM(wugga);
- if (is_in_list(enabled_list,n_enabled,blargh))
- remove_from_list(enabled_list,&n_enabled,blargh);
- else
- add_to_list(enabled_list,&n_enabled,blargh);
- }
- else { //-- button
- //-- the only thing to watch out for is if they
- // change their mind about the basic part of
- // the chord
- if (chord_item_pressed<=HI_7TH_CHORD
- && guessed_basic_part) { //--replace basic part
- for (i=0; i<n_guess; i++) {
- if (guess[i]<=HI_7TH_CHORD)
- remove_from_list(guess,&n_guess,guess[i]);
- }
- }
- if (!is_in_list(guess,n_guess,chord_item_pressed)
- || chord_item_pressed<=HI_7TH_CHORD) {
- add_to_list(guess,&n_guess,chord_item_pressed);
- if (chord_item_pressed<=HI_7TH_CHORD) {
- guessed_basic_part = 1;
- activate_and_deactivate = 1;
- }
- }
- else {
- remove_from_list(guess,&n_guess,chord_item_pressed);
- }
- changed_guess = 1;
- need_to_redraw = 1;
- }
- }
- }//--end if they hit a valid control
- break;
- case complete_window_erase:
- they_have_chord_to_consider = 0;
- first_time = 1;
- chord_window_exists = 0;
- if (about_chord_window_exists && VALID_POINTER(where_is_about_chord_complete_window))
- delete where_is_about_chord_complete_window;
- return;
- }
-
- if (pressed_ok && guessed_basic_part) {
- int actual_chord[MAX_CHORD_NOTES],guess_chord[MAX_CHORD_NOTES];
- int n_actual_notes,n_guess_notes;
-
- activate_and_deactivate = 1; //-- now that they've pressed ok, dim it
- guessed_basic_part = 0; // ...
-
- make_chord(actual_chord, &n_actual_notes, item, n_items);
- make_chord(guess_chord, &n_guess_notes, guess, n_guess);
-
- right = same_canonical_form(actual_chord,n_actual_notes,
- guess_chord,n_guess_notes);
- //-- do they have the same canonical form?
-
- ++ntries;
- nright += right;
- if (right) {
- current_icon = CHECK_ICON_ID;
- }
- else {
- current_icon = X_ICON_ID;
- }
- they_have_chord_to_consider = 0;
- they_gave_answer = 1;
- need_to_redraw = 1;
- if (!have_previous_chord && !made_about_chord_window) {
- made_about_chord_window = 1;
- make_about_chord_window();
- SelectWindow(my_complete_window->the_window);
- // bring attention back to me!
- }
- have_previous_chord = 1;
- previous_root = root;
- previous_n_notes = n_notes;
- copy_chord(previous_chord,chord,n_notes);
- if (right) {
- //-- If they gave the right answer, that is what we
- // want to put down as the description of the previous
- // chord. I.e., if they gave a valid alternative
- // description of the chord, we don't want them
- // to think they got it wrong.
- int try_root,new_items[MAX_CHORD_ITEMS],new_n_items;
- previous_n_items = n_guess;
- copy_chord(previous_item,guess,n_guess);
- //-- what root is implied by their guess?
- for (try_root=0; try_root<=11; try_root++) {
- analyze_chord_based_on_this_root(
- new_items,&new_n_items,chord,n_notes,try_root);
- if (new_n_items!=0 &&
- lists_are_same_except_for_order(
- new_n_items,new_items,n_guess,guess)) {
- previous_root = try_root;
- break;
- }
- }
- }
- else {
- previous_n_items = n_items;
- copy_chord(previous_item,item,n_items);
- }
- //-- let about_chord_den_mother know it should redraw itself
- if (about_chord_window_exists) {
- GrafPtr save_graf;
- WindowPtr window;
- window = where_is_about_chord_complete_window->the_window;
-
- SelectWindow(window);
-
- GetPort(&save_graf);
- SetPort(window);
-
- EraseRect(&window->portRect);
- DrawControls(window);
-
- where_is_about_chord_complete_window->whassup
- = complete_window_redraw;
- (*(where_is_about_chord_complete_window->den_mother))
- (where_is_about_chord_complete_window);
-
- SetPort(save_graf);
-
- SelectWindow(my_complete_window->the_window);
- // bring attention back to me!
- }
- }
-
- if (need_to_redraw) {
- if (!ready_to_update) {
- GetPort(&save_graf);
- SetPort(my_complete_window->the_window);
- }
- if (activate_and_deactivate || need_to_redraw_all) {
- if (guessed_basic_part)
- activate_ctl_by_nifty_label(my_complete_window->the_window,
- "ok",1,*decoder_string);
- else
- deactivate_ctl_by_nifty_label(my_complete_window->the_window,
- "ok",1,*decoder_string);
- if (have_previous_chord)
- activate_ctl_by_nifty_label(my_complete_window->the_window,
- "play_previous",1,*decoder_string);
- else
- deactivate_ctl_by_nifty_label(my_complete_window->the_window,
- "play_previous",1,*decoder_string);
- }
- if (twiddle_radio_buttons || need_to_redraw_all) {
- set_ctl_by_nifty_label(my_complete_window->the_window,
- "cho",1,*decoder_string,(chord_or_arpeggio==1));
- set_ctl_by_nifty_label(my_complete_window->the_window,
- "ar",1,*decoder_string,(chord_or_arpeggio==2));
- }
- if (twiddle_check_box || need_to_redraw_all) {
- int yowza,this_chord_item,nifty_index;
- char nifty_name[30];
- for (yowza=0; yowza<=MAXPART; yowza++) { //-- normally we break out of inside of loop
- part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,yowza);
- if (nifty_index < 0) break;
- this_chord_item = part_num_to_chord_item(yowza,*decoder_string);
- if (this_chord_item!=0 && IS_CHECK_BOX(this_chord_item)) {
- set_ctl_by_nifty_label(my_complete_window->the_window,
- nifty_name,nifty_index,*decoder_string,
- is_in_list(enabled_list,n_enabled,
- PLAIN_CHORD_ITEM(this_chord_item)));
- }
- }
- }
- if (need_to_redraw_all || redraw_inversion_check_box) {
- set_ctl_by_nifty_label(my_complete_window->the_window,
- "inversions",1,*decoder_string,inversions_allowed);
- }
- if (have_previous_chord
- && (need_to_redraw_all || they_gave_answer)) {
- char s[200];
- Handle h;
- if (1) { //-- new method of showing right answer: guitar chord symbol
- get_item_by_nifty_label(my_complete_window->the_window,
- "correct_answer",1,*decoder_string,&h);
- if (VALID_HANDLE(h)) {
- describe_chord(s+1,previous_n_items,previous_item);
- s[0] = strlen(s+1);
- TextSize((short) 18);
- TextFont(geneva);
- SetIText(h,(unsigned char *) s);
- normal_text_style();
- }
- }
- }
- if (they_gave_answer && !right) {
- if (0) { //-- old method of showing right answer: erase buttons
- int i,this_chord_item,nifty_index;
- char nifty_name[30];
- Handle h;
- need_to_redraw_erased_buttons = 1;
- for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
- part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
- if (nifty_index < 0) break;
- this_chord_item = part_num_to_chord_item(i,*decoder_string);
- if (this_chord_item!=0 && !IS_CHECK_BOX(this_chord_item)
- && !is_in_list(item,n_items,this_chord_item)) {
- hide_ctl_by_nifty_label(my_complete_window->the_window,
- nifty_name,nifty_index,*decoder_string);
- }
- }
- }
- }
- if (need_to_redraw_all) {
- normal_text_style();
- refresh_text(my_complete_window,"t1",1,*decoder_string);
- refresh_text(my_complete_window,"t2",1,*decoder_string);
- refresh_text(my_complete_window,"t3",1,*decoder_string);
- refresh_text(my_complete_window,"t4",1,*decoder_string);
- refresh_text(my_complete_window,"t5",1,*decoder_string);
- normal_text_style();
- }
- if (need_to_redraw_all || changed_difficulty) {
- {
- Handle h;
- char s[50];
- Rect r;
- short x,y;
- PolyHandle arrowhead;
- get_item_by_nifty_label(my_complete_window->the_window,
- "d",1,*decoder_string,&h);
- if (VALID_HANDLE(h)) {
- sprintf(s+1,"difficulty: %d",(int) raw_difficulty);
- s[0] = strlen(s+1);
- SetIText(h,(unsigned char *) s);
- refresh_text(my_complete_window,"d",1,*decoder_string);
- }
- }
- }
- if (they_gave_answer || cleared_scores || need_to_redraw_all) {
- Handle h;
- char s[50];
- int i,tot;
- get_item_by_nifty_label(my_complete_window->the_window,
- "sc",1,*decoder_string,&h);
- if (VALID_HANDLE(h)) {
- if (ntries>0) {
- sprintf(s+1,"%d %c",(int) ((100.*nright)/((double) ntries)),'%');
- s[0] = strlen(s+1);
- }
- else {
- sprintf(s+1," ");
- s[0] = strlen(s+1);
- }
- TextSize((short) 18);
- TextFont(geneva);
- SetIText(h,(unsigned char *) s);
- normal_text_style();
- }
- }
- if (they_gave_answer || cleared_scores || need_to_redraw_all) {
- Handle hh;
- Rect rr;
- hh = GetIcon((short) current_icon);
- if (VALID_HANDLE(hh)) {
- get_rect_by_nifty_label(my_complete_window->the_window,
- "ic",1,*decoder_string, &rr);
- PlotIcon(&rr,hh);
- }
- }
- if (they_gave_answer && !right) {
- int i,this_chord_item,nifty_index;
- char nifty_name[30];
- //-- old method of showing correct answer was to erase other buttons;
- //-- now we have to redraw them:
- if (need_to_redraw_erased_buttons) {
- rest((int)( 2. * 2000.*60./typical_tempo));
- for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
- part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
- if (nifty_index < 0) break;
- this_chord_item = part_num_to_chord_item(i,*decoder_string);
- if (this_chord_item!=0 && !IS_CHECK_BOX(this_chord_item)
- && !is_in_list(item,n_items,this_chord_item)) {
- show_ctl_by_nifty_label(my_complete_window->the_window,
- nifty_name,nifty_index,*decoder_string);
- }
- }
- }
- }
- if (!ready_to_update) {
- SetPort(save_graf);
- }
- }//---end if need to redraw
- if (!they_have_chord_to_consider) {
- chose_new_chord = 1;
- choose_chord(item,&n_items,difficulty,enabled_list,n_enabled,
- min_notes,max_notes);
- make_chord(chord,&n_notes,item,n_items);
- root = 0; //-- this gets changed below when we shift the
- // whole chord to its final pitch, and
- // also in case we reinterpret the chord
- fiddle_with_chord_voicing(chord,n_notes,inversions_allowed);
- //-- kludge: if it's a m7 with 3 in bass, reinterpret as a 6 chord
- if (n_notes==4 && notes_to_quick_label(chord,n_notes)==2131) {
- if (make_0_to_11(chord[0]-root)==3) {
- n_items = 2;
- item[0] = CHORD_MAJ_TRIAD;
- item[1] = CHORD_6;
- make_chord(chord,&n_notes,item,n_items);
- root = 0;
- }
- }
- center = mean_note+(random_double()-.5)*6+(random_double()-.5)*6;
- avg = avg_chord(chord,n_notes);
- for (i=0; i<n_notes; i++) {
- chord[i] += center-avg;
- }
- root += center-avg;
- duration = choose_duration(typical_tempo);
- if (random_double()<.5) {
- arpeggio_direction = 1;
- }
- else {
- arpeggio_direction = -1;
- }
- play_it = 1;
- they_have_chord_to_consider = 1;
- n_guess = 0;
- guessed_basic_part = 0;
- }
- if (play_it || play_previous) {
- if (play_bass) {
- play_chord(1,my_snd_chan,chord,duration,1);
- }
- else {
- int chord_to_play[MAX_CHORD_NOTES],play_n_notes;
- if (play_it) {
- copy_chord(chord_to_play,chord,n_notes);
- play_n_notes = n_notes;
- }
- else { //-- play previous chord
- copy_chord(chord_to_play,previous_chord,previous_n_notes);
- play_n_notes = previous_n_notes;
- }
- if (chord_or_arpeggio==1 && !play_arpeggio) {
- play_chord(play_n_notes,my_snd_chan,chord_to_play,duration,1);
- }
- else {
- int i,x[4],d,start,stop;
- if (chord_or_arpeggio==1) //--we're really set to play chords, so speed
- // up for arpeggio
- d = duration*((double) TYPICAL_TEMPO_CHORD )
- /((double) TYPICAL_TEMPO_ARPEGGIO);
- else
- d = duration;
- if (arpeggio_direction>0) {
- start = 0;
- stop = play_n_notes-1;
- }
- else {
- start = play_n_notes-1;
- stop = 0;
- }
- i = start;
- for (;;) {
- x[0] = chord_to_play[i];
- play_chord(1,my_snd_chan,x,d,1);
- if (i==stop) break;
- i += arpeggio_direction;
- }
- }
- }
- }
-
- //--- A second block of code for updating the window: the chord symbol
- // for their guess. The reason it has to be down here is that
- // if we're moving on to the next chord, we wait until now to
- // erase their previous guess, _after_ playing the new chord
- if (chose_new_chord || changed_guess || need_to_redraw_all) {
- Handle h;
- char s[50];
- int i;
- if (!ready_to_update) {
- GetPort(&save_graf);
- SetPort(my_complete_window->the_window);
- }
- get_item_by_nifty_label(my_complete_window->the_window,
- "guess",1,*decoder_string,&h);
- if (VALID_HANDLE(h)) {
- describe_chord(s+1,n_guess,guess);
- s[0] = strlen(s+1);
- TextSize((short) 18);
- TextFont(geneva);
- SetIText(h,(unsigned char *) s);
- normal_text_style();
- }
- if (!ready_to_update) {
- SetPort(save_graf);
- }
- }
-
-
-
- }
-
- double
- scale_difficulty(int raw)
- {
- double z,y;
- z = raw;
- if (z>20.) z=20.;
- y = .3*z*z+z-1.;
- if (z>=10.) y += 1000.*((1./(21.-z))-(1./11.));
- return y;
- }
-
- unsigned int
- plain_chord_item(unsigned int x)
- {
- unsigned int y;
- y = 0x7FFFL;
- return x & y;
- }
-
- int
- part_num_to_chord_item(int part,char *decoder_string)
- {
- char nifty_name[50];
- int nifty_index;
- part_number_to_nifty_label(nifty_name,&nifty_index,decoder_string,part);
- if (strcmp(nifty_name,"majt")==0) return CHORD_MAJ_TRIAD;
- if (strcmp(nifty_name,"mint")==0) return CHORD_MIN_TRIAD;
- if (strcmp(nifty_name,"dimt")==0) return CHORD_DIM_TRIAD;
- if (strcmp(nifty_name,"augt")==0) return CHORD_AUG_TRIAD;
- if (strcmp(nifty_name,"sust")==0) return CHORD_SUS_TRIAD;
- if (strcmp(nifty_name,"b5t")==0) return CHORD_FLAT5_TRIAD;
- if (strcmp(nifty_name,"7")==0) return CHORD_DOM7;
- if (strcmp(nifty_name,"m7")==0) return CHORD_MIN7;
- if (strcmp(nifty_name,"maj7")==0) return CHORD_MAJ7;
- if (strcmp(nifty_name,"o7")==0) return CHORD_DIM7;
- if (strcmp(nifty_name,"h")==0) return CHORD_HALF_DIM;
-
- if (strcmp(nifty_name,"b9")==0) return CHORD_FLAT9;
- if (strcmp(nifty_name,"9")==0) return CHORD_9;
- if (strcmp(nifty_name,"#9")==0) return CHORD_SHARP9;
- if (strcmp(nifty_name,"11")==0) return CHORD_11;
- if (strcmp(nifty_name,"#11")==0) return CHORD_SHARP11;
- if (strcmp(nifty_name,"b6")==0) return CHORD_FLAT6;
- if (strcmp(nifty_name,"6")==0) return CHORD_6;
- if (strcmp(nifty_name,"a7")==0) return CHORD_ADD7;
- if (strcmp(nifty_name,"amaj7")==0) return CHORD_ADDMAJ7;
-
- if (strcmp(nifty_name,"no1")==0) return CHORD_NO1;
- if (strcmp(nifty_name,"no3")==0) return CHORD_NO3;
- if (strcmp(nifty_name,"no5")==0) return CHORD_NO5;
-
- if (strcmp(nifty_name,"cmajt")==0) return CHORD_MAJ_TRIAD | 0x8000;
- if (strcmp(nifty_name,"cmint")==0) return CHORD_MIN_TRIAD | 0x8000;
- if (strcmp(nifty_name,"cdimt")==0) return CHORD_DIM_TRIAD | 0x8000;
- if (strcmp(nifty_name,"caugt")==0) return CHORD_AUG_TRIAD | 0x8000;
- if (strcmp(nifty_name,"csust")==0) return CHORD_SUS_TRIAD | 0x8000;
- if (strcmp(nifty_name,"cb5t")==0) return CHORD_FLAT5_TRIAD | 0x8000;
- if (strcmp(nifty_name,"c7")==0) return CHORD_DOM7 | 0x8000;
- if (strcmp(nifty_name,"cm7")==0) return CHORD_MIN7 | 0x8000;
- if (strcmp(nifty_name,"cmaj7")==0) return CHORD_MAJ7 | 0x8000;
- if (strcmp(nifty_name,"co7")==0) return CHORD_DIM7 | 0x8000;
- if (strcmp(nifty_name,"ch")==0) return CHORD_HALF_DIM | 0x8000;
-
- if (strcmp(nifty_name,"cb9")==0) return CHORD_FLAT9 | 0x8000;
- if (strcmp(nifty_name,"c9")==0) return CHORD_9 | 0x8000;
- if (strcmp(nifty_name,"c#9")==0) return CHORD_SHARP9 | 0x8000;
- if (strcmp(nifty_name,"c11")==0) return CHORD_11 | 0x8000;
- if (strcmp(nifty_name,"c#11")==0) return CHORD_SHARP11 | 0x8000;
- if (strcmp(nifty_name,"cb6")==0) return CHORD_FLAT6 | 0x8000;
- if (strcmp(nifty_name,"c6")==0) return CHORD_6 | 0x8000;
- if (strcmp(nifty_name,"ca7")==0) return CHORD_ADD7 | 0x8000;
- if (strcmp(nifty_name,"camaj7")==0) return CHORD_ADDMAJ7 | 0x8000;
-
- if (strcmp(nifty_name,"cno1")==0) return CHORD_NO1 | 0x8000;
- if (strcmp(nifty_name,"cno3")==0) return CHORD_NO3 | 0x8000;
- if (strcmp(nifty_name,"cno5")==0) return CHORD_NO5 | 0x8000;
-
- return 0;
- }
-
- //
- // Play with chord voicing.
- // If inversions_allowed is false, leave root in bass. Assume chord[0] is root.
- // Chord is always returned as a sorted list, lowest note first.
- //
- void
- fiddle_with_chord_voicing(int *chord,int n,int inversions_allowed)
- {
- int i,lowest,height_above_bass,height_above_lower_neighbor,
- height_below_upper_neighbor,nearest_neighbor,z,
- changed_voicing,j,min_height;
- int used_up[MAX_CHORD_NOTES];
- if (inversions_allowed)
- lowest = 0;
- else
- lowest = 1;
- do {
- for (j=0; j<n; j++) {
- used_up[j] = 0;
- }
- for (j=lowest; j<n; j++) {
- do {
- i = -1 + lowest + random_integer(n-lowest);
- } while(used_up[i]);
- used_up[i] = 1;
-
- changed_voicing = 0;
- if (i>0) {
- height_above_lower_neighbor = chord[i]-chord[i-1];
- height_above_bass = chord[i]-chord[0];
- }
- else {
- height_above_lower_neighbor = 0;
- height_above_bass = 999;
- }
- if (i<n-1)
- height_below_upper_neighbor = chord[i+1]-chord[i];
- else
- height_below_upper_neighbor = 999;
- if (height_below_upper_neighbor<height_above_lower_neighbor)
- nearest_neighbor=height_below_upper_neighbor;
- else
- nearest_neighbor=height_above_lower_neighbor;
- if (12.+random_double()*6.<height_above_lower_neighbor) {
- chord[i] -= 12;
- changed_voicing = 1;
- }
- else {
- if (nearest_neighbor<3 && random_double()*3.>nearest_neighbor
- && random_double()<.5) {
- chord[i] += 12;
- changed_voicing = 1;
- }
- else {
- switch(random_integer(3)) {
- case 1: min_height = 0; break;
- case 2: min_height = 3; break;
- case 3: min_height = 5; break;
- }
- if (height_above_bass<=min_height) {
- chord[i] += 12;
- changed_voicing = 1;
- }
- }
- }
- if (changed_voicing)
- sort_int_list(chord,n);
-
-
- }
- } while(random_double()<.3);
- }
-
- // large difficulty (say 999) means all chords equally likely
- // difficulty of 7 means strangest chords 7 times less likely than most normal
- // Note that what we are passed is the _scaled_ difficulty.
- void
- choose_chord(int *item,int *n,double difficulty,int *enabled_list,int n_enabled,
- int min_notes,int max_notes)
- {
- int z,what,chord[MAX_CHORD_NOTES],new_chord[MAX_CHORD_NOTES],
- n_notes,diss,new_n_notes,seal_of_approval,
- save_items[MAX_CHORD_NOTES],save_n_items,bogus,i,
- qqq;
-
- do { //--- outer loop checks if simplification gives something they disabled
-
- do { //--- until we find a chord that's not too dissonant
-
- //---- first, choose the basic chord quality
- do {
- do {
- z = random_double()*13.;
- } while (z<1 || z>11);
- switch(z) {
- case 1: what = CHORD_MAJ_TRIAD; break;
- case 2: what = CHORD_MIN_TRIAD; break;
- case 3: what = CHORD_DIM_TRIAD; break;
- case 4: what = CHORD_AUG_TRIAD; break;
- case 5: what = CHORD_SUS_TRIAD; break;
- case 6: what = CHORD_FLAT5_TRIAD; break;
- case 7: what = CHORD_DOM7; break;
- case 8: what = CHORD_MIN7; break;
- case 9: what = CHORD_MAJ7; break;
- case 10: what = CHORD_DIM7; break;
- case 11: what = CHORD_HALF_DIM; break;
- }
- }while (
- (random_double()*difficulty+1.<how_unusual_is_basic_chord(what)
- && random_double()<.8)
- || !is_in_list(enabled_list,n_enabled,what) );
- item[0] = what;
- *n = 1;
- make_chord(chord,&n_notes,item,*n);
-
- //---- now, choose some added tones; use plenty, because it
- // can always get rejected later for being too dissonant
- while (random_double()<.9 && random_double()*(.3*difficulty+1.2)>1.
- && n_notes<=max_notes+3) {
- do {
- do {
- z = random_double()*14.;
- } while (z<1 || z>12);
- switch(z) {
- case 1: what = CHORD_FLAT9; break;
- case 2: what = CHORD_9; break;
- case 3: what = CHORD_SHARP9; break;
- case 4: what = CHORD_11; break;
- case 5: what = CHORD_SHARP11; break;
- case 6: what = CHORD_FLAT6; break;
- case 7: what = CHORD_6; break;
- case 8: what = CHORD_ADD7; break;
- case 9: what = CHORD_ADDMAJ7; break;
- case 10: what = CHORD_NO1; break;
- case 11: what = CHORD_NO3; break;
- case 12: what = CHORD_NO5; break;
- }//--end switch
- make_chord(chord,&n_notes,item,*n);
- new_n_notes = n_notes;
- if (!is_in_list(item,*n,what)) {
- add_to_list(item,n,what);
- make_chord(new_chord,&new_n_notes,item,*n);
- remove_from_list(item,n,what);
- }
- diss = dissonance(new_chord,new_n_notes);
- seal_of_approval =
- !(
- random_double()*difficulty<how_unusual_is_added_tone(what)
- || !is_in_list(enabled_list,n_enabled,what)
- || new_n_notes==n_notes
- );
- } while (!seal_of_approval
- && random_double()>.05 //--make sure it doesn't loop forever
- );
- //--while loop for picking one that's not too unusual
- if (seal_of_approval) {
- add_to_list(item,n,what);
- make_chord(chord,&n_notes,item,*n);
- }
- else
- break;
- }//--while loop for adding one tone after another
-
- //-- Special cases for combinations of items that don't really
- // make sense:
- bogus = (*n==2 && item[1]==CHORD_NO1)
- //... without root, becomes a triad
- || (*n==2 && item[0]==CHORD_DIM7 && item[1]>=LO_OMITTED_TONE
- && item[1]<=HI_OMITTED_TONE)
- //...=dim triad
- || (*n==2 && item[0]==CHORD_HALF_DIM && item[1]==CHORD_NO3)
- //... =b5 triad
- ;
-
- make_chord(chord,&n_notes,item,*n);
- diss = dissonance(chord,n_notes);
- qqq = random_double()*difficulty;
- while (random_double()<.2) qqq += 1.; //-- avoid infinite loop on low
- // difficulty
- } while(qqq<2.*diss-1.
- || bogus || n_notes<min_notes || n_notes>max_notes);
- //--end outer while loop; if too dissonant, try again
-
- //
- // Now simplify the chord. But if simplification results in a
- // reduction of number of items by one or less, and the simplified
- // version uses disabled items, go back to the more complicated
- // version.
- //
-
- copy_chord(save_items,item,*n);
- save_n_items = *n;
-
- simplify_chord(item,n);
-
- bogus = 0;
- for (i=0; i<*n; i++) {
- bogus = bogus || !is_in_list(enabled_list,n_enabled,item[i]);
- }
-
- if (bogus && *n>=save_n_items-1) {
- bogus = 0;
- copy_chord(item,save_items,save_n_items);
- *n = save_n_items;
- }
-
- } while (bogus);
- }
-
-
- void
- make_about_chord_window()
- {
- int previous_n_windows;
- if (!about_chord_window_exists) {
- previous_n_windows = n_windows;
- make_window(ABOUT_CHORD_DLOG_ID,"\p","\p","\p","\p",
- &about_chord_den_mother);
- if (n_windows==previous_n_windows+1) {
- where_is_about_chord_complete_window = my_windows[n_windows-1];
- about_chord_window_exists = 1;
- }
- else {
- where_is_about_chord_complete_window = (complete_window *) 0;
- about_chord_window_exists = 0;
- }
- }
- }
-
-
-
-
-
-
-